#ifndef CNC_COMPILER_PROGRAM_DATABASE_IMPLEMENTATION_H
#define CNC_COMPILER_PROGRAM_DATABASE_IMPLEMENTATION_H

// provided interface
#include "progmgr.h"
// basic component model
#include <bcm/bcm.h>
// implementation headers
#include <list>
#include <fstream>
#include <windows.h>
#ifdef _UNICODE
#include <utf8conv.h>
#endif

namespace cnc {

	namespace compiler {

		class ProgramDatabaseImpl : public bcm::Identifier<ProgramManager>
		{
		protected:
			std::string path_;
			std::list<ProgramData> listing_;
			std::list<ProgramData>::iterator it;
			bool generateListing()
			{
				WIN32_FIND_DATA findFileData;
				HANDLE hFind = INVALID_HANDLE_VALUE;
//				DWORD dwError;
				if(!listing_.empty())
					listing_.clear();
				hFind = FindFirstFile((LPCTSTR)(path_ + std::string("*")).c_str(), &findFileData);
				if (hFind == INVALID_HANDLE_VALUE) 
					return false;
				while (FindNextFile(hFind, &findFileData)) {
#ifdef _UNICODE
					std::wstring utf16 = findFileData.cFileName;
					std::string utf8 = utf8util::utf8_from_utf16(utf16);
#else
					std::string utf8 = findFileData.cFileName;
#endif
					listing_.push_back(ProgramData(path_ + utf8));
				}
				FindClose(hFind);
				return true;
			}
		protected:
			ProgramDatabaseImpl(const char *path = "c:\\Proof\\")
				: bcm::Identifier<ProgramManager>("progmgr", "Program Manager"),
				path_(path) {}
			virtual ProgramManager *getProgramManager()
			{
				return this;
			}	
		protected: // Program Manager
			void setPath(const char *path)
			{
				path_ = path;
			}
			ProgramData *getFirstProgramData()
			{
				if(generateListing())
				{
					it = listing_.begin();
					return &(*it);
				}
				else
					return NULL;
			}
			ProgramData *getNextProgramData()
			{
				it++;
				if (it == listing_.end())
					return NULL;
				else
				{
					return &(*it);
				}
			}
			std::istream *getInputStream(const char *name)
			{
				std::string fullpath = path_ + name;
				return new std::ifstream(fullpath.c_str());
			}
			void releaseInputStream(std::istream *is)
			{
				dynamic_cast<std::ifstream *>(is)->close();
				delete dynamic_cast<std::ifstream *>(is);
			}
			std::ostream *getOutputStream(const char *name)
			{
				std::string fullpath = path_ + name;
				return new std::ofstream(fullpath.c_str());
			}
			void releaseOutputStream(std::ostream *os)
			{
				dynamic_cast<std::ofstream *>(os)->close();
				delete dynamic_cast<std::ostream *>(os);
			}
			bool renameProgram(const char *oldname, const char *newname)
			{
				if (rename((path_ + oldname).c_str(), (path_ + newname).c_str()) == 0)
					return true;
				else
					return false;
			}
			bool removeProgram(const char *filename)
			{
				if (remove((path_ + filename).c_str()) == 0)
					return true;
				else
					return false;
			}
			void copyProgram(const char *copyFilename, const char *sourceFilename)
			{
				std::ifstream infile((path_ + sourceFilename).c_str(), std::ios_base::binary);
				std::ofstream outfile((path_ + copyFilename).c_str(), std::ios_base::binary);
				outfile << infile.rdbuf();
			}
		};

	} // namespace compiler

} // namespace cnc

#endif
